﻿// <copyright file="Timer.cs" company="Community Combat Simulator">
//    Community Combat Simulator: a free combat flight simulator.
//    Copyright (C) 2008 Aaron Schinkowitch
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see http://www.gnu.org/licenses/.
// </copyright>
namespace CCSim.Util
{
    using System;
    using System.Runtime.InteropServices;

    /// <summary>
    /// A timer is used to track time within the engine with a high resolution.
    /// </summary>
    public class Timer
    {
        /// <summary>
        /// How many ticks compose a second.
        /// </summary>
        private double ticksPerSecond;
        
        /// <summary>
        /// The tick value when the timer was started.
        /// </summary>
        private long startTick;
        
        /// <summary>
        /// Initializes a new instance of the Timer class that is not started.
        /// </summary>
        public Timer()
        {    
        }
        
        /// <summary>
        /// Gets the current time. If the timer hasn't been started, the time is zero. Otherwise,
        /// the current time is the number of seconds since the timer was started.
        /// </summary>
        /// <value>The number of seconds since the timer was started.</value>
        public double Time 
        {
            get
            {
                if (this.TicksPerSecond == 0.0) 
                {
                    // Timer hasn't been started yet
                    return 0.0;
                }
                
                return (this.GetCurrentTick() - this.StartTick) / this.TicksPerSecond;
            }
        }

        /// <summary>
        /// Gets or sets the ticks per second.
        /// </summary>
        internal double TicksPerSecond 
        {
            get { return this.ticksPerSecond; }
            set { this.ticksPerSecond = value; }
        }
        
        /// <summary>
        /// Gets or sets the start tick.
        /// </summary>
        private long StartTick 
        {
            get { return this.startTick; }
            set { this.startTick = value; }
        }
        
        /// <summary>
        /// Starts the timer. This will restart the timer if the timer has previously been started.
        /// </summary>
        public void Start()
        {
            if (this.TicksPerSecond == 0.0)
            {
                this.TicksPerSecond = this.GetTicksPerSecond();    
            }
            
            this.StartTick = this.GetCurrentTick();
        }
        
        /// <summary>
        /// Gets the number of ticks per second for the native timer.
        /// </summary>
        /// <returns>The number of ticks per second.</returns>
        internal virtual long GetTicksPerSecond()
        {
            long frequency = 0;
            
            bool success = NativeMethods.QueryPerformanceFrequency(ref frequency);
            
            if (!success)
            {
                throw new PlatformNotSupportedException("Unable to query performance frequency");
            }
            
            return frequency;
        }
        
        /// <summary>
        /// Gets the current tick value from the native timer.
        /// </summary>
        /// <returns>The current tick value.</returns>
        internal virtual long GetCurrentTick()
        {
            long tick = 0;
            
            bool success = NativeMethods.QueryPerformanceCounter(ref tick);
            
            if (!success)
            {
                throw new PlatformNotSupportedException("Unable to query performance counter");
            }
            
            return tick;
        }
        
        /// <summary>
        /// Native methods for querying the timer.
        /// </summary>
        private class NativeMethods
        {
            /// <summary>
            /// Prevents a default instance of the NativeMethods class from being created.
            /// </summary>
            private NativeMethods()
            {
            }
            
            // [System.Security.SuppressUnmanagedCodeSecurity]
            [DllImport("kernel32")]
            internal static extern bool QueryPerformanceFrequency(ref long frequency);
    
            // [System.Security.SuppressUnmanagedCodeSecurity]
            [DllImport("kernel32")]
            internal static extern bool QueryPerformanceCounter(ref long tick);
        }
    }
}
